package org.arclib.x.camera;

import org.arclib.Window;
import org.arclib.math.Point;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;

public class Camera
{
	public Camera(Window argW)
	{
		window = argW;
	}

	// / update camera-space mouse coordinates
	public void process()
	{
		// transform mouse pos
		Point normalized_mouse = new Point(Mouse.getX() / window.getSize().w,
				Mouse.getY() / window.getSize().h);
		mousePos = normalized_to_camera(normalized_mouse);

		// get view area
		viewPos = normalized_to_camera(Point.Zero);
		viewSize = normalized_to_camera(new Point(1, 1)).sub(viewPos);
	}

	// / add position to camera
	public void move(Point v)
	{
		pos.addEquals(v);
	}

	// /
	public void moveLeft(double v)
	{
		pos.x -= v;
	}

	// /
	public void moveRight(double v)
	{
		pos.x += v;
	}

	// /
	public void moveUp(double v)
	{
		pos.y -= v;
	}

	// /
	public void moveDown(double v)
	{
		pos.y += v;
	}

	// / set position of camera to point
	public void setPosition(Point p)
	{
		pos = p;
	}

	// / get position of camera
	public Point getPosition()
	{
		return pos;
	}

	// / get mouse position
	public Point getMousePos()
	{
		return mousePos;
	}

	// / set zoom amount
	public void setZoom(double argZoom)
	{
		zoom = argZoom;
	}

	// / get zoom amount
	public double getZoom()
	{
		return zoom;
	}

	// /
	public void zoomIn(double d)
	{
		zoom -= d;
	}

	// /
	public void zoomOut(double d)
	{
		zoom += d;
	}

	// / set angle amount
	public void setAngle(double argAngle)
	{
		angle = argAngle;
	}

	// / get angle amount
	public double getAngle()
	{
		return angle;
	}

	// / Set GL projection settings with glOrtho
	public void setProjection(double argLeft, double argRight,
			double argBottom, double argTop)
	{
		left = argLeft;
		right = argRight;
		bottom = argBottom;
		top = argTop;
	}

	// / start viewing transformations
	public void open()
	{
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();

		double width = right - left;
		double height = bottom - top;
		double mid_x = (left + right) / 2.;
		double mid_y = (top + bottom) / 2.;
		GL11.glOrtho(mid_x - zoom * width / 2., mid_x + zoom * width / 2.,
				mid_y + zoom * height / 2., mid_y - zoom * height / 2., -1.0f,
				1.0f);

		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();

		GL11.glDisable(GL11.GL_DEPTH_TEST);
		GL11.glEnable(GL11.GL_TEXTURE_2D);

		GL11.glPushMatrix();

		// rotate
		// glRotatef(angle, 0,0,1);

		// translate to position (can be center of screen)
		GL11.glTranslated(-pos.x, -pos.y, 0);
	}

	// / transforms a point
	public Point camera_to_normalized(Point from)
	{
		Point extend = new Point(right - left, bottom - top).mult(zoom);
		Point invextends = new Point(1 / extend.x, 1 / extend.y);
		Point mid = new Point((left + right) / 2., (top + bottom) / 2.);

		from.subEquals(pos);
		from.rotate(angle);
		from.subEquals(mid);
		from.scale(invextends);
		from.addEquals(new Point(0.5, 0.5));

		return from;
	}

	// / un-transforms a point
	public Point normalized_to_camera(Point from)
	{
		Point extend = new Point(right - left, bottom - top).mult(zoom);
		Point mid = new Point((left + right) / 2., (top + bottom) / 2.);

		from.subEquals(new Point(0.5, 0.5));
		from.scale(extend);
		from.addEquals(mid);
		from.rotate(-angle);
		from.addEquals(pos);

		return from;
	}

	// / end viewing transformations
	public void close()
	{
		GL11.glPopMatrix();
	}

	// / get camera view position
	public Point getViewPos()
	{
		return viewPos;
	}

	// / get camera viewing size
	public Point getViewSize()
	{
		return viewSize;
	}

	private Point pos = Point.Zero;
	private double zoom = 1;
	private double angle = 0;
	private double left = 0, right = 0, bottom = 0, top = 0;
	private Window window;

	// the mouse position in this camera's coordinate system, updated through
	// process()
	private Point mousePos;

	// BUG: Does not work with rotation
	// the lower left point of the view area
	private Point viewPos;
	// the size of the view area
	private Point viewSize;
}
